package org.kotemaru.gae.dstool.client;
import java.io.*;
import java.net.*;
import java.util.*;

import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.EncodedKeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import org.apache.log4j.Logger;
import org.mortbay.log.Log;

import net.oauth.client.OAuthClient;
import net.oauth.OAuthServiceProvider;
import net.oauth.OAuthConsumer;
import net.oauth.OAuthAccessor;
import net.oauth.OAuth;
import net.oauth.ParameterStyle;
import net.oauth.OAuthMessage;
import net.oauth.OAuthException;
import net.oauth.http.HttpClient;
import net.oauth.http.HttpMessage;
import net.oauth.signature.*;


import net.oauth.client.httpclient3.HttpClient3;
import net.oauth.client.URLConnectionClient;

public class GaeOAuthClient {
	static final Logger LOG = Logger.getLogger(GaeOAuthClient.class);
	
	public static final String APP_ID             = "appid";
	public static final String CONSUMER_SECRET    = "secret";
	public static final String OAUTH_TOKEN        = "oauth_token";
	public static final String OAUTH_TOKEN_SECRET = "oauth_token_secret";
	public static final String PRIVATE_KEY = "private_key";
	public static final String STATUS = "status";

	public static final String APP_NAME           = "application_name";

	public static final String GET_REQ_TOKEN    = "/_ah/OAuthGetRequestToken";
	public static final String GET_AUTH_TOKEN   = "/_ah/OAuthAuthorizeToken";
	public static final String GET_ACCESS_TOKEN = "/_ah/OAuthGetAccessToken";

	public static final String HTTPS      = "https://";
	public static final String APPSPOT_COM      = "appspot.com";

	private Properties props;
	private String appId;
	private String consumerSecret;
	private String domain;
	private	String reqUrl    ;
	private	String authUrl   ;
	private	String accessUrl ;

 	private	OAuthAccessor accessor;
	private	OAuthClient client;
       
	public GaeOAuthClient(Properties p) throws Exception {
		props = p;
		appId  = props.getProperty(APP_ID);
		consumerSecret = props.getProperty(CONSUMER_SECRET);
		domain = appId + "." + APPSPOT_COM;
		reqUrl    = HTTPS + domain + GET_REQ_TOKEN;
		authUrl   = HTTPS + domain + GET_AUTH_TOKEN;
		accessUrl = HTTPS + domain + GET_ACCESS_TOKEN;

		accessor = getOAuthAccessor();
		client = getOAuthClient();
	}

	protected OAuthAccessor getOAuthAccessor() throws Exception {
		OAuthServiceProvider provider = 
			new OAuthServiceProvider(reqUrl, authUrl, accessUrl);
		OAuthConsumer consumer = 
			new OAuthConsumer(null, domain, consumerSecret, provider);

		String key = props.getProperty(PRIVATE_KEY);
		if (key != null) {
			EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(
				OAuthSignatureMethod.decodeBase64(key)
			);
			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
			PrivateKey privateKey = keyFactory.generatePrivate(privKeySpec);
			consumer.setProperty(RSA_SHA1.PRIVATE_KEY, privateKey);
			consumer.setProperty(OAuth.OAUTH_SIGNATURE_METHOD, OAuth.RSA_SHA1);
		}

		return new OAuthAccessor(consumer);
	}

	protected OAuthClient getOAuthClient() {
		OAuthClient client = new OAuthClient(new HttpClient3());
		client.getHttpParameters().put(HttpClient.FOLLOW_REDIRECTS, Boolean.TRUE);
		return client;
	}
	protected OAuthMessage send(String url, String token) throws Exception {
		List<OAuth.Parameter> params = new ArrayList<OAuth.Parameter>();
		//params.add(new OAuth.Parameter(APP_NAME, appId));
		params.add(new OAuth.Parameter(OAUTH_TOKEN, token));
		OAuthMessage response = client.invoke(accessor, "GET",  url, params);
		return response;
	}

	public String getAuthUrl() throws Exception {
		client.getRequestToken(accessor);
		OAuthMessage response = send(authUrl, accessor.requestToken);

		props.setProperty(OAUTH_TOKEN, accessor.requestToken);
 		props.setProperty(OAUTH_TOKEN_SECRET, accessor.tokenSecret);
		return response.URL;
	}
	public void access() throws Exception {
		accessor.tokenSecret = props.getProperty(OAUTH_TOKEN_SECRET);
		OAuthMessage response = send(accessUrl, props.getProperty(OAUTH_TOKEN));

		props.setProperty(OAUTH_TOKEN, response.getParameter(OAUTH_TOKEN));
		props.setProperty(OAUTH_TOKEN_SECRET,
				response.getParameter(OAUTH_TOKEN_SECRET));
	}

 	public OAuthMessage doGet(String url) throws Exception {
		accessor.consumer.setProperty(OAuthClient.PARAMETER_STYLE, 
								ParameterStyle.AUTHORIZATION_HEADER);
		accessor.tokenSecret = props.getProperty(OAUTH_TOKEN_SECRET);

		List<OAuth.Parameter> params = new ArrayList<OAuth.Parameter>();
		//params.add(new OAuth.Parameter(APP_NAME, appId));
		params.add(new OAuth.Parameter(OAUTH_TOKEN, props.getProperty(OAUTH_TOKEN)));
		OAuthMessage response = client.invoke(accessor, "GET",  url, params);
		return response;
	}


 	public String getOAuthHeader(String method, String url) throws Exception {
		accessor.consumer.setProperty(OAuthClient.PARAMETER_STYLE, 
				ParameterStyle.AUTHORIZATION_HEADER);
		accessor.tokenSecret = props.getProperty(OAUTH_TOKEN_SECRET);

		List<OAuth.Parameter> params = new ArrayList<OAuth.Parameter>();
		//params.add(new OAuth.Parameter(APP_NAME, appId));
		params.add(new OAuth.Parameter(OAUTH_TOKEN, props.getProperty(OAUTH_TOKEN)));
 		OAuthMessage request = 
        	accessor.newRequestMessage(method, url, params);
 		//System.out.println("----->"+OAuthSignatureMethod.getBaseString(request));
 		return request.getAuthorizationHeader(null);
 	}
 	
//------------------------------------------------------------------

	public static String prepare(String fname, String appid, String secret) 
		throws Exception
	{
		Properties props = ClientUtil.loadProps(fname, null, false);
		GaeOAuthClient client = new GaeOAuthClient(props);
		props.setProperty(APP_ID, appid);
		props.setProperty(CONSUMER_SECRET, secret);
		props.setProperty(STATUS, "init");
		ClientUtil.saveProps(fname, props);
		String url = client.getAuthUrl();
		
		props.setProperty(STATUS, "auth");
		ClientUtil.saveProps(fname, props);
		return url;
	}

	public static void finish(String fname) throws Exception {
		Properties props = ClientUtil.loadProps(fname, null, true);
		String status = props.getProperty(STATUS);
		if ("auth".equals(status)) {
			GaeOAuthClient client = new GaeOAuthClient(props);
			client.access();
			props.setProperty(client.STATUS, "access");
			ClientUtil.saveProps(fname, props);
		}
	}
	

	public static void main(String[] args)  throws Exception {
		String propFile = args[0];
		if (args.length == 1) {
			String url = prepare(propFile, args[1], args[2]);
			System.out.println("Auth URL:\n"+url);
		} else if (args[1].equals("access")) {
			finish(propFile);
		} else {
			Properties props = ClientUtil.loadProps(propFile, null, true);
			GaeOAuthClient test = new GaeOAuthClient(props);
			OAuthMessage response = test.doGet(args[1]);
			System.out.println(response.readBodyAsString());
		}
	}

}
